#include "libc/stdint.h"

#define FW_CFG_SIGNATURE    0x0000
#define FW_CFG_ID           0x0100
#define FW_CFG_FILE_DIR     0x1900


typedef struct FWCfgFile {	/* an individual file entry, 64 bytes total */
    uint32_t size;		/* size of referenced fw_cfg item, big-endian */
    uint16_t select;		/* selector key of fw_cfg item, big-endian */
    uint16_t reserved;
    char name[56];		/* fw_cfg item name, NUL-terminated ascii */
}FWCfgFile;

typedef struct FWCfgFiles {		/* the entire file directory fw_cfg item */
    uint32_t count;		/* number of entries, in big-endian format */
    struct FWCfgFile f[30];	/* array of file entries, see below */
}FWCfgFiles;

typedef struct RAMDBCFG
{
    uint64_t addr;
    uint32_t fourcc;
    uint32_t flags;
    uint32_t width;
    uint32_t height;
    uint32_t stride;
}RAMDBCFG;



typedef struct fwcfg{
    uint64_t Data;
    uint16_t Selector;
    uint16_t Reserved1;
    uint32_t Reserved2;
    uint64_t DMA;
}fwcfg;

uint32_t big_to_small_endian32(uint32_t big){
    return ((big & 0xFF) << 24) | ((big & (0xFF << 8)) << 8) | ((big & (0xFF << 16)) >> 8)| ((big & (0xFF << 24)) >> 24);
}

uint64_t big_to_small_endian64(uint64_t big){
    return ((uint64_t)big_to_small_endian32(big >> 32)) | ((uint64_t)big_to_small_endian32(big)) << 32;
    // return ((uint64_t)big_to_small_endian32(big >> 32)) << 32 | ((uint64_t)big_to_small_endian32(big));
    // return big;
}


struct fwcfg* fwcfg_reg_map = (struct fwcfg*) 0x9020000;



void fwcfg_init(){
    printk("\nfwcfg_reg_map: %x\n", fwcfg_reg_map);
    fwcfg_reg_map->Selector = FW_CFG_SIGNATURE;
    
    uint64_t bytes = fwcfg_reg_map->Data;
    char signature[5];
    signature[0] = ( bytes & 0xFF);
    signature[1] = ((bytes >> 8 ) & 0xFF);
    signature[2] = ((bytes >> 16) & 0xFF);
    signature[3] = ((bytes >> 24) & 0xFF);
    signature[4] = '\0';
    if(strcmp(signature, "QEMU")){
        printk("fw_cfg device is present\n");
    }

    if(fwcfg_reg_map->DMA == 0x47464320554D4551){
        printk("DMA interface is available\n");
    }


    fwcfg_reg_map->Selector = FW_CFG_FILE_DIR;

    FWCfgFiles files;

    files.count = big_to_small_endian64(fwcfg_reg_map->Data) >> 32;



    for(uint32_t i = 0; i < files.count; ++i){
        uint64_t tmp;


        tmp = big_to_small_endian64(fwcfg_reg_map->Data);

        printk("tmp: %x\n", tmp);

        files.f[i].size = tmp & 0xFFFFFFFF;

        files.f[i].select = (tmp>>48) & 0xFFFF;


        for(uint32_t k = 0;k < 7; ++k){
            tmp = fwcfg_reg_map->Data;
            for(int p = 0; p < 8; ++p){
                files.f[i].name[ 8 * k + p ] = (tmp >> ( 8 * p)) & 0xFF;
            }
        }

    }

    printk("count: %d\n", files.count);
    for(uint32_t i = 0; i < files.count; ++i){
        printk("size%d: %x      ",i , files.f[i].size);
        printk("select%d: %x      ",i , files.f[i].select);
        printk("name%d: %s\n",i , files.f[i].name);
    }


    fwcfg_reg_map->Selector = 0x2A00;

    printk("addr: %x\n", fwcfg_reg_map->Data);
    printk("addr: %x\n", fwcfg_reg_map->Data);
    printk("addr: %x\n", fwcfg_reg_map->Data);
    printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    // printk("addr: %x\n", fwcfg_reg_map->Data);
    
    RAMDBCFG ramcfg;

    ramcfg.addr = big_to_small_endian64(fwcfg_reg_map->Data);

    uint64_t tmp = big_to_small_endian64(fwcfg_reg_map->Data);

    ramcfg.fourcc = (tmp >> 32) & 0xFFFFFFFF;
    ramcfg.flags = (tmp) & 0xFFFFFFFF;


    tmp = big_to_small_endian64(fwcfg_reg_map->Data);

    ramcfg.width = (tmp >> 32) & 0xFFFFFFFF;
    ramcfg.height = (tmp) & 0xFFFFFFFF;


    tmp = big_to_small_endian64(fwcfg_reg_map->Data);

    ramcfg.stride = (tmp >> 32) & 0xFFFFFFFF;


    printk("addr: %x\n", ramcfg.addr);
    printk("fourcc: %d\n", ramcfg.fourcc);
    printk("flags: %d\n", ramcfg.flags);
    printk("width: %d\n", ramcfg.width);
    printk("height: %d\n", ramcfg.height);
    printk("stride: %d\n", ramcfg.stride);

    // uint64_t* ss = (uint64_t* )ramcfg.addr;

    // ss[0] = 0x71;




    
    // fwcfg_print_map();
}


void fwcfg_print_map(){
    printk("------ print fwcfg -----\n");
    printk("Data: %x(%b)\n", &fwcfg_reg_map->Data, fwcfg_reg_map->Data);
    // printk("Selector: %x(%b)\n", &fwcfg_reg_map->Selector, fwcfg_reg_map->Selector);
    printk("DMA: %x(%b)\n", &fwcfg_reg_map->DMA, fwcfg_reg_map->DMA);
    printk("------ end fwcfg -----\n");
}
